Quiz (Week 7)
Table of Contents
1 Applicatives
1.1 Question 1
Which of the following type definitions are examples of Applicative
?
- ✔
Maybe
- ✗
String
- ✔
(->) a
for anya
- ✗
(,) a
for anya
- ✔
[ ]
- ✔
Gen
As usual, String
is not a type constructor, so cannot be an Applicative
. Furthermore (,) a
does not admit a law-abiding applicative instance either, as we cannot implement pure
:
instance Applicative ((,) x) where pure :: a -> (x, a) pure a = (??? , a)
Of the other options, Gen
, Maybe
, (->) a
and [ ]
. The latter three can be implemented as follows:
instance Applicative Maybe where pure x = Just x Just f <*> Just x = Just (f x) _ <*> _ = Nothing instance Applicative ((->) x) where pure a = \x -> a (xf <*> xa) x = xf x (xa x) instance Applicative [ ] where pure a = [ a ] [] <*> ys = [] (f:fs) <*> xs = map f xs ++ (fs <*> xs)
1.2 Question 2
Consider the following data type definition for a non-empty list in Haskell.
data NonEmptyList a = One a | Cons a (NonEmptyList a)
Which of the following are law-abiding Applicative
instances for NonEmptyList
?
- ✔
instance Applicative NonEmptyList where pure x = Cons x (pure x) (One f) <*> (One x) = One (f x) (One f) <*> (Cons x _) = One (f x) (Cons f _) <*> (One x) = One (f x) (Cons f fs) <*> (Cons x xs) = Cons (f x) (fs <*> xs)
- ✗
instance Applicative NonEmptyList where pure x = One x (One f) <*> (One x) = One (f x) (One f) <*> (Cons x _) = One (f x) (Cons f _) <*> (One x) = One (f x) (Cons f fs) <*> (Cons x xs) = Cons (f x) (fs <*> xs)
- ✔
instance Applicative NonEmptyList where pure x = One x One f <*> xs = fmap f xs (Cons f fs) <*> xs = fmap f xs `append` (fs <*> xs) where append (One x) ys = Cons x ys append (Cons x xs) ys = Cons x (xs `append` ys)
- ✗
instance Applicative NonEmptyList where pure x = Cons x (pure x) One f <*> xs = fmap f xs (Cons f fs) <*> xs = fmap f xs `append` (fs <*> xs) where append (One x) ys = Cons x ys append (Cons x xs) ys = Cons x (xs `append` ys)
Option 3 is analogous to the same Applicative
instance we knows from regular lists, so is a valid Applicative
instance.
Options 2 and 4 don't obey the first Applicative
law, pure id <*> v = v
.
Option 1 is also a valid applicative instance, analogous to the ZipList
instance available in the Haskell standard library.
1.3 Question 3
Suppose I wanted to write a function pair
, which takes two Applicative
data structures and combines them in a tuple, of type:
pair :: (Applicative f) => f a -> f b -> f (a, b)
Select all correct implementations of pair
.
- ✗
pair fa fb = pure fa <*> pure fb
- ✗
pair fa fb = pure (,) <*> pure fa <*> pure fb
- ✔
pair fa fb = pure (,) <*> fa <*> fb
- ✔
pair fa fb = fmap (,) fa <*> fb
- ✗There is no way to implement this function.
Options 1 and 2 are not type correct.
Options 3 and 4 are both correct, and equivalent, as fmap f x = (pure f <*> x)
, if the Applicative
and Functor
instances
are law-abiding.
2 Monads
2.1 Question 4
Which of the following type definitions are examples of Monad
, or admit
law-abiding Monad
instances?
- ✔
Maybe
- ✗
String
- ✔
(->) a
for anya
- ✗
(,) a
for anya
- ✔
[ ]
- ✔
Gen
Monad instances can be written for Maybe
, (->) a
and [ ]
, and Gen
is an
abstract type that also implements Monad
. The rest have:
instance Monad Maybe where Just x >>= f = f x Nothing >>= f = Nothing instance Monad ((->) x) where (xa >>= axb) = \x -> axb (xa x) x instance Monad [ ] where xs >>= each = concatMap each xs
2.2 Question 5
We wish to write a function s
of type [m a] -> m [a]
, for any monad m
.
It will unpack each given value of type m a
and collect their results into a list.
Which of the following is a correct implementation
of this function?
- ✗
s :: Monad m => [m a] -> m [a] s [] = [] s (a:as) = do x <- a xs <- s as return (x : xs)
- ✗
s :: Monad m => [m a] -> m [a] s [] = return [] s (a:as) = do x <- a xs <- as return (x : xs)
- ✔
s :: Monad m => [m a] -> m [a] s [] = return [] s (a:as) = do x <- a xs <- s as return (x : xs)
- ✗
s :: Monad m => [m a] -> m [a] s [] = return [] s (a:as) = do a s as return (a : as)
Only answer 3 is type correct.
3 Functor, Monad, Applicative
3.1 Question 6
Which of the following are correct definitions of the usual <*>
operation
for the type [a]
?
- ✔
fs <*> xs = do f <- fs x <- xs return (f x)
- ✔
fs <*> xs = [f x | f <- fs, x <- xs]
- ✔
fs <*> xs = fs >>= \f -> xs >>= \x -> [f x]
- ✗
fs <*> xs = fs >>= \f -> xs >>= \x -> f x
Answers 1, 2 and 3 are three different syntactic forms expressing the same operation. However, Answer 4 is not type correct.
3.2 Question 7
Recall that two functions are considered equal if they have the same output for every input. Select all the true statements below.
- ✗Kleisli composition is not well-defined in the
Maybe
monad. - ✗Every monad
m
and every functionf :: a -> m a
satisfies the equalityf <=< f = f
. - ✗Every instance of
Applicative
has to be an instance ofMonad
as well. - ✔Every instance of
Monad
has to be an instance ofFunctor
as well.
Answer 4 is correct. Answer 1 is not, since Kleisli composition is well-defined in every monad.
Answer 2 is not correct (think about e.g. the list monad). Answer 3 is not correct
either: while every Applicative
has to be a Functor
, they need not be instances of Monad
.
3.3 Question 8
If a given unary type m
is both a Monad
instance and a Functor
instance, which of the following would
definitely be correct implementations of fmap :: (a -> b) -> m a -> m b
?
- ✗
fmap f [] = [] fmap f (x:xs) = map f xs
- ✔
fmap f = (<$>) f
- ✗
fmap f xs = f <*> xs
- ✔
fmap f xs = xs >>= \x -> return (f x)
The implementations 1 and 3 do not (or may not) type-check. The others are correct.